package org.xiaov.lunasql.engine.transform.oracle;

import com.alibaba.druid.sql.ast.SQLLimit;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
import org.dromara.hutool.core.text.StrUtil;
import org.xiaov.lunasql.engine.ast.AdjustContext;
import org.xiaov.lunasql.engine.ast.IntervalAdjust;
import org.xiaov.lunasql.engine.ast.LimitAdjust;
import org.xiaov.lunasql.engine.ast.WithRecursiveAsAdjust;
import org.xiaov.lunasql.engine.function.MySqlFunction;
import org.xiaov.lunasql.engine.keyword.DatabaseKeywords;

import java.util.List;

/**
 * <p>
 * LunaMysql2OracleAtsVisitorAdapter
 * </p>
 *
 * @author LunaSQL
 * @since 2024/12/11 22:39
 */
public class Mysql2OracleAtsVisitorAdapter extends MySqlASTVisitorAdapter {
    private final List<MySqlFunction> functions;
    private final LimitAdjust limitAdjust;
    private final DatabaseKeywords databaseKeywords;
    private final WithRecursiveAsAdjust withRecursiveAsAdjust;
    private final IntervalAdjust intervalAdjust;

    /**
     * 构造函数
     *
     * @param functions        functions
     * @param adjustContext    adjustContext
     * @param databaseKeywords databaseKeywords
     */
    public Mysql2OracleAtsVisitorAdapter(List<MySqlFunction> functions,
                                         AdjustContext adjustContext,
                                         DatabaseKeywords databaseKeywords) {
        this.functions = functions;
        this.databaseKeywords = databaseKeywords;
        this.limitAdjust = adjustContext.getLimitAdjust();
        this.withRecursiveAsAdjust = adjustContext.getWithRecursiveAsAdjust();
        this.intervalAdjust = adjustContext.getIntervalAdjust();
    }

    @Override
    public boolean visit(SQLMethodInvokeExpr x) {
        //进行函数的转换
        for (MySqlFunction function : functions) {
            if (function.accept(x)) {
                function.transform(x);
                break;
            }
        }
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLAggregateExpr x) {
        //进行函数的转换
        for (MySqlFunction function : functions) {
            if (function.accept(x)) {
                function.transform(x);
            }
        }
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLLimit x) {
        limitAdjust.adjust(x);
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLIntervalExpr x) {
        intervalAdjust.adjust(x);
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLIdentifierExpr x) {
        String originalName = x.getName();
        if (databaseKeywords.getKeywords().contains(originalName.toUpperCase())) {
            x.setName(databaseKeywords.getKeywordEscapeChar()
                    + originalName.toLowerCase()
                    + databaseKeywords.getKeywordEscapeChar());
        }
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLWithSubqueryClause x) {
        if (x.getRecursive() != null && x.getRecursive()) {
            withRecursiveAsAdjust.adjust(x);
        }
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLWithSubqueryClause.Entry x) {
        withRecursiveAsAdjust.adjustEntry(x);
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLSelectItem x) {
        String alias = x.getAlias();
        if (StrUtil.isNotEmpty(alias)) {
            alias = solveKeyword(alias);
            x.setAlias(alias);
        }
        return super.visit(x);
    }

    @Override
    public boolean visit(SQLPropertyExpr x) {
        String owner = solveKeyword(x.getOwner().toString());
        String name = solveKeyword(x.getName());
        x.setOwner(owner);
        x.setName(name);
        return super.visit(x);
    }

    /**
     * 处理关键字
     *
     * @param field 字段
     * @return 处理关键字后的字段
     */
    private String solveKeyword(String field) {
        if (databaseKeywords.getKeywords().contains(field.toLowerCase())
                || databaseKeywords.getKeywords().contains(field.toUpperCase())) {
            return databaseKeywords.getKeywordEscapeChar()
                    + field.toLowerCase()
                    + databaseKeywords.getKeywordEscapeChar();
        }
        return field;
    }

}
